#include "Base.h"
#include "GameEngine.h"

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

void free_resources (int wfd, int rfd, unsigned char * step_decoded,
    unsigned char ** board) {
  send_to (wfd, 0xFF);
  close (wfd);
  close (rfd);
  free (step_decoded);

  int i;
  if (board == NULL)
    return;

  for (i = 0; i < 3; i++)
    if (board[i] == NULL)
      return;
    else
      free (board[i]);

  free (board);
}

/*
 * Returns:
 * 1 if it need to send data on this iteration;
 * 2 if it need to receive data on this iteration;
 * 0 if it need to stop iterations on this iteration;
 * -1 if there are incorrect data.
 */ 
int check_iterator (int i, short order) {
  if (i == 9)
    return 0;

  if (order == 1) {
    if (i % 2)
      return 2;
    else
      return 1;
  } else {
    if (i % 2)
      return 1;
    else
      return 2;
  }
}

// argv[1] - fifo for reading,
// argv[2] - fifo for writing,
// argv[3] - order of the step.
// "x"s should play first.
int main (int argc, char ** argv) {
  if (argc != 4) {
    printf ("%s: wrong number of args\n", argv[0]);
    return 0;
  }

  if (strcmp(argv[3], "x") != 0 && strcmp (argv[3], "o") != 0) {
    printf ("%s: wrong order of step\n", argv[0]);
    return 0;
  }

  if (mkfifo (argv[1], 0777) == -1 && errno != EEXIST) {
    printf ("%s: can't make a fifo called %s\n", argv[0], argv[1]);
    return 0;
  }

  if (mkfifo (argv[2], 0777) == -1 && errno != EEXIST) {
    printf ("%s: can't make a fifo called %s\n", argv[0], argv[2]);
    return 0;
  }

  int read_fd, write_fd;
  unsigned char my_char, other_char;
  short order;

  if (strcmp (argv[3], "x") == 0) {
    order = 1;
    my_char = 'x';
    other_char = 'o';
  } else {
    order = 2;
    my_char = 'o';
    other_char = 'x';
  }

  if (order == 1) {
    if ((read_fd = open(argv[1], O_RDONLY)) == -1) {
      printf ("%s: can't open a fifo called %s\n", argv[0], argv[2]);
      return 0;
    }

    if ((write_fd = open(argv[2], O_WRONLY)) == -1) {
      printf ("%s: can't open a fifo called %s\n", argv[0], argv[1]);
      close (read_fd);
      return 0;
    }
  } else {
    if ((write_fd = open(argv[1], O_WRONLY)) == -1) {
      printf ("%s: can't open a fifo called %s\n", argv[0], argv[1]);
      close (read_fd);
      return 0;
    }

    if ((read_fd = open(argv[2], O_RDONLY)) == -1) {
      printf ("%s: can't open a fifo called %s\n", argv[0], argv[2]);
      return 0;
    }

  }

  // This is a game board.
  // Each field can take one of the following values:
  // x, o, _ (means that field isn't set).
  unsigned char ** board = (unsigned char **) malloc (sizeof (char *) * 3);
  unsigned char * step_decoded = (unsigned char *) malloc (sizeof(char) * 3);
  unsigned char step_encapsed; 
  int i;

  for (i = 0; i < 3; i++) {
    board[i] = (unsigned char *) malloc (sizeof(char) * 3);
    memset (board[i], '_', sizeof(char) * 3);
  }

  if (strcmp (argv[3], "x") == 0) {
    order = 1;
    my_char = 'x';
    other_char = 'o';
  } else {
    order = 2;
    my_char = 'o';
    other_char = 'x';
  }
  printf ("Let's start the game!\n");

  // number of steps perfomed.
  i = 0;
  if (order == 1) {
    scanf ("%c %c %c", &step_decoded[0], &step_decoded[1], &step_decoded[2]);

    if (encode_step (step_decoded, &step_encapsed) > -1)
      send_to (write_fd, step_encapsed);
    else {
      free_resources (write_fd, read_fd, step_decoded, board);
      return 0;
    }

    if (perform_step (board, step_decoded) < 0) {
      free_resources (write_fd, read_fd, step_decoded, board);
      return 0;
    }

    paint (board);
    i++;
  }

  while (check_iterator (order, i)) {
     if (check_iterator (order, i) == 2) {
      // receive branch.

      // possible if something crached.
      if (receive_from (read_fd, &step_encapsed) < 0) {
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      if (decode_step (step_encapsed, step_decoded) < 0) {
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      if (perform_step (board, step_decoded) < 0) {
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      paint (board);

      if (won(board, my_char)) {
        printf ("You won!\n");
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      if (won(board, other_char)) {
        printf ("You lost!\n");
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }
    } else if (check_iterator (order, i) == 1) {
      // send branch.

      scanf ("%c %c %c", &step_decoded[0], &step_decoded[1], &step_decoded[2]);

      if (encode_step (step_decoded, &step_encapsed) > -1) {
        if (send_to (write_fd, step_encapsed) < 0) {
          free_resources (write_fd, read_fd, step_decoded, board);
        }
      } else {
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      if (perform_step (board, step_decoded) < 0) {
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      paint (board);

      if (won(board, my_char)) {
        printf ("You won!\n");
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }

      if (won(board, other_char)) {
        printf ("You lost!\n");
        free_resources (write_fd, read_fd, step_decoded, board);
        return 0;
      }
    }
    i++;
  }

  printf ("Draw!\n");
  free_resources (write_fd, read_fd, step_decoded, board);

  return 0;
}
